package win.doyto.service.data.report.support;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.servlet.View;
import win.doyto.service.data.config.ReportConfigEntity;
import win.doyto.service.data.config.ReportParam;
import win.doyto.service.data.report.AbstractReportView;
import win.doyto.service.data.report.ReportMapper;
import win.doyto.service.data.report.ReportService;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;

import static win.doyto.service.data.report.ReportMapper.SQL_KEY;

/**
 * ReportJsonService
 *
 * @author f0rb on 2017-12-20.
 */
@Slf4j
@Service
public class ReportJsonService implements ReportService {
    @Resource
    private ReportMapper reportMapper;

    @Override
    @Transactional
    public String fetchAndWrite(OutputStream output, List<ReportConfigEntity> reportConfigEntities, Map<String, Object> params) throws IOException {
        if (reportConfigEntities.size() == 1) {
            return writeValue(output, reportConfigEntities.get(0), params);
        }

        return multiSql(output, reportConfigEntities, params);
    }

    public String multiSql(OutputStream output, List<ReportConfigEntity> reportConfigEntities, Map<String, Object> params) {
        try {
            output.write('{');
            int index = 0;
            for (ReportConfigEntity reportConfigEntity : reportConfigEntities) {
                ReportParam reportParam = reportConfigEntity.reportParam();

                if (reportParam.isContentTypeSupported("json")) {
                    writeProperty(output, reportParam, index);
                    writeValue(output, reportConfigEntity, params);
                    index++;
                    if (index < reportConfigEntities.size()) {
                        output.write(',');
                    }
                }
            }
            output.write('}');
        } catch (IOException e) {
            log.error("OutputStream", e);
        }

        return reportConfigEntities.get(0).getReportName();
    }

    private String writeValue(OutputStream output, ReportConfigEntity reportConfigEntity, Map<String, Object> params) throws IOException {
        ReportParam reportParam = reportConfigEntity.reportParam();
        params.put(SQL_KEY, reportConfigEntity.getReportSql());
        Iterator<LinkedHashMap<String, Object>> iterator = reportMapper.getData(params).iterator();
        if (reportParam.isSingleValue()) {
            output.write(JSON.toJSONBytes(iterator.next().values().iterator().next()));
        } else if (reportParam.isSingleRow()) {
            output.write(JSON.toJSONBytes(iterator.next()));
        } else {
            output.write('[');
            if (iterator.hasNext()) {
                output.write(JSON.toJSONBytes(iterator.next()));
                while (iterator.hasNext()) {
                    output.write(',');
                    output.write(JSON.toJSONBytes(iterator.next()));
                }
            }
            output.write(']');
        }
        return reportParam.getJsonPropertyName();
    }

    private void writeProperty(OutputStream output, ReportParam reportParam, int index) throws IOException {
        output.write('\"');
        String propertyName = StringUtils.defaultIfBlank(reportParam.getJsonPropertyName(), "data" + index);
        output.write(propertyName.getBytes());
        output.write('\"');
        output.write(':');
    }

    @Override
    public String getExtension() {
        return "json";
    }

    @Bean
    View jsonView(ReportService reportJsonService) {
        return new AbstractReportView(reportJsonService, "application/json;charset=UTF-8");
    }

}
